package com.swf.seed.jolt;

import org.dom4j.Document;
import org.dom4j.io.SAXReader;
import org.dom4j.*;
import java.util.List;

/**
 * <p>Title: Jolt调用的配置文件</p>
 * <p>Description: 与Tuxedo服务连接的相关配置信息,包括长连接配置信息,短连接配置信息.
 *     <p>使用前可以先使用 <code>JoltConfig.configure( configFile );</code>来设置配置并进行配置文件的加载,
 *     <br/>如果不进行配置,默认为从JVM参数 -Djolt.configuration变量中读取配置文件,
 *     <br/>如果JVM参数中也不设置,则会从当前根目录下面的JoltConfig.xml文件中读取.</p>
 * </p>
 * <p>Copyright: Copyright (c) 2009</p>
 * <p>Company: Beijing Watchdata CO,.Ltd</p>
 * @author da.wang
 * @version 1.0
 */
public class JoltConfig {

    private static int[] lock = new int[0];
    private static String cfgFilePath = "";

    private static BasicJoltConfig basicConfig = null;

    private static PoolJoltConfig poolConfig = null;

    // 是否是用连接池
    private volatile boolean poolEnable = false;

    private volatile static JoltConfig cfg = null;

    public PoolJoltConfig getPoolConfig() {
        return poolConfig;
    }

    public BasicJoltConfig getBasicConfig() {
        return basicConfig;
    }

    public void setPoolEnable( boolean poolEnable ) {
        this.poolEnable = poolEnable;
    }

    public void setPoolConfig( PoolJoltConfig poolConfig ) {
        this.poolConfig = poolConfig;
    }

    public void setBasicConfig( BasicJoltConfig basicConfig ) {
        this.basicConfig = basicConfig;
    }

    public boolean isPoolEnable() {
        return this.poolEnable;
    }

    private JoltConfig() {
    }

    /**
     * 设置配置文件路径并加载配置文件
     * @param cfgFilePath String
     * @exception WDJoltException 如果加载配置文件数据出错
     */
    public static void configure( String cfgFilePath )
        throws WDJoltException {
        JoltConfig.cfgFilePath  = cfgFilePath;
        if( JoltConfig.cfgFilePath == null || JoltConfig.cfgFilePath.equals( "" ) ) {
            throw new IllegalArgumentException( "*******Jolt Configuration is null" );
        }
        // 加载配置文件
        getInstance();
    }

    /**
     * 直接使用配置类来对系统进行配置
     * @param cfg JoltConfig
     */
    public static void configure( JoltConfig cfg ) {
        if( cfg == null || cfg.getBasicConfig() == null || cfg.getPoolConfig() == null ) {
            throw new IllegalArgumentException( "*******JoltConfig is null!" );
        }
        JoltConfig.cfg = cfg;
    }

    /**
     * 得到配置实例
     * @throws WDJoltException 如果是第一次加载配置文件,并且加载配置时出错
     * @return JoltConfig 配置实例
     */
    public static JoltConfig getInstance()
        throws WDJoltException {
        if ( cfg == null ) {
            synchronized( lock ) {
                if ( cfg == null ) {
                    JoltConfig tmp = new JoltConfig();
                    tmp.loadConfig();
                    cfg = tmp;
                }
            }
        }
        return cfg;
    }

    /**
     * 得到一个新的,空的配置类,需要手动设置配置项
     * @return JoltConfig 没有任何配置数据
     */
    static public JoltConfig newInstance() {
        return new JoltConfig();
    }

    /**
     * 加从配置文件中加载配置数据
     * @throws WDJoltException 如果加载配置失败
     */
    public void loadConfig()
        throws WDJoltException {
        Document doc = null;
        SAXReader reader = new SAXReader();
        try {
            if ( cfgFilePath == "" && System.getProperty( "jolt.configuration" ) != null ) {
                cfgFilePath = System.getProperty( "jolt.configuration" );
            }

            if( cfgFilePath == "" ) {
                cfgFilePath = ".\\JoltConfig.xml";
            }
            doc = reader.read( cfgFilePath );
        }
        catch( DocumentException ex ) {
            throw new WDJoltException( "*******Load Jolt configuration error( " + cfgFilePath + " )", ex );
        }

        this.poolEnable = readNodeValue( doc, "//Root/Jolt/usePool", "false" ).equals( "true" )? true:false;

        // 读取短连接配置
        this.basicConfig = readBasicConfig( doc );

        // 读取池连接配置
        this.poolConfig = readPoolConfig( doc );
    }

    /*
     * 得到一个结点的值（也可以是属性）
     * @param doc Document　Document对象
     * @param path String xpath路径
     * @param def String　没有取到结点后返回的默认值
     * @return String　结点的值
     * @exception WDJoltException 如果读取结点出错
     */
    private String readNodeValue( Node doc, String path, String def )
        throws WDJoltException {
        try {
            Node node = doc.selectSingleNode( path );
            if( node == null ) {
                return def;
            }

            String value = node.getText();

            return value == null ? def : value;
        }
        catch( Exception ex ) {
            throw new WDJoltException( "*******Read XML node error:" + path, ex );
        }
    }

    /*
     * 得到一个结点的值（也可以是属性）
     * @param doc Document　Document对象
     * @param path String xpath路径
     * @param def int　没有取到结点后返回的默认值
     * @return int　结点的值
     * @exception WDJoltException 如果读取结点出错
     */
    private int readNodeValue( Node doc, String path, int def )
        throws WDJoltException {
        try {
            Node node = doc.selectSingleNode( path );
            if( node == null ) {
                return def;
            }
            String value = node.getText();
            return value == null || value.equals( "" ) ? def :
                Integer.parseInt( value );
        }
        catch( Exception ex ) {
            throw new WDJoltException( "*******Read XML node error:" + path, ex );
        }
    }

    private BasicJoltConfig readBasicConfig( Document doc )
        throws WDJoltException {
        BasicJoltConfig cfg = new BasicJoltConfig();
        cfg.setAppAddress( readNodeValue( doc, "//Root/Jolt/commonSetting/appAddress", "" ) );
        cfg.setAppPassword( readNodeValue( doc, "//Root/Jolt/commonSetting/appPassword ", "" ) );
        cfg.setUserName( readNodeValue( doc, "//Root/Jolt/commonSetting/userName ", "" ) );
        cfg.setUserPassword( readNodeValue( doc, "//Root/Jolt/commonSetting/userPassword ", "" ) );
        cfg.setUserRole( readNodeValue( doc, "//Root/Jolt/commonSetting/userRole ", "" ) );

        cfg.setRecvTimeout( readNodeValue( doc, "//Root/Jolt/commonSetting/recvTimeout", 0 ) );
        cfg.setSendTimeout( readNodeValue( doc, "//Root/Jolt/commonSetting/sendTimeoutd", 0 ) );
        cfg.setSessionTimeout( readNodeValue( doc, "//Root/Jolt/commonSetting/sessionTimeout", 0 ) );
        cfg.setIdleTimeout( readNodeValue( doc, "//Root/Jolt/commonSetting/idleTimeout", 0 ) );

        return cfg;
    }

    private PoolJoltConfig readPoolConfig( Document doc )
        throws WDJoltException {
        List lst = null; // 临时列表
        PoolJoltConfig cfg = new PoolJoltConfig();
        List cfgLst = doc.selectNodes( "//Root/Jolt/poolSetting/poolItem" );

        Node node = null;
        PoolConfigItem item = null;
        for ( int i=0; i<cfgLst.size(); i++ ) {
            node = (Node)cfgLst.get( i );
            item = new PoolConfigItem();
            item.setAppPassword( readNodeValue( node, "//appPassword", "" ) );
            item.setUserName( readNodeValue( node, "//userName", "" ) );
            item.setUserPassword( readNodeValue( node, "//userPassword", "" ) );
            item.setUserRole( readNodeValue( node, "//userRole", "" ) );
            String poolName = readNodeValue( node, "//poolName", "" );
            // 如果名字为空,使用默认的名
            item.setPoolName( poolName.equals("")?null:poolName );
            item.setRecvTimeout( readNodeValue( node, "//recvTimeout", 0 ) );
            item.setMinPoolSize( readNodeValue( node, "//minPoolSize", 0 ) );
            item.setMaxPoolSize( readNodeValue( node, "//maxPoolSize", 0 ) );

            String[] addr = null;
            // 首选地址
            lst = node.selectNodes( "//primaryAddr/addr" );
            addr = new String[ lst.size() ];
            for( int j=0; j<lst.size(); j++ ) {
                addr[i] = ( (Node)lst.get(j) ).getText();
            }
            item.setPrimaryAddrList( addr );

            // 备选地址
            lst = node.selectNodes( "//secondaryAddr/addr" );
            addr = new String[ lst.size() ];
            for( int j=0; j<lst.size(); j++ ) {
                addr[j] = ( (Node)lst.get(j) ).getText();
            }
            item.setSecondaryAddrList( addr );

            cfg.addPoolItem( item );
        }
        return cfg;
    }

}
